#ifndef TCP_BUFFER_H
#define TCP_BUFFER_H

#include <cstring>
#include <cassert>
#include <algorithm>
#include <string>
#include <vector>

using std::string;

/**
 * 简易封装的字符串封装类
 */
class StringArg {
public:
    StringArg(const char *str)
            : ptr_(str), length_(static_cast<int>(strlen(ptr_))) {}

    StringArg(const string &str)
            : ptr_(str.c_str()), length_(static_cast<int>(str.size())) {}

    const char *data() const { return ptr_; }

    int size() const { return length_; }

private:
    const char *ptr_;
    int length_;
};

class Buffer {
public:
    static const size_t kCheapPrepend = 8;

    static const size_t kInitialSize = 1024;

    explicit Buffer(size_t initialSize = kInitialSize)
            : buffer_(kCheapPrepend + initialSize),
              readerIndex_(kCheapPrepend),
              writerIndex_(kCheapPrepend) {
        assert(readableBytes() == 0);
        assert(writableBytes() == initialSize);
        assert(prependableBytes() == kCheapPrepend);
    }

    // implicit copy-ctor, move-ctor, dtor and assignment are fine
    // NOTE: implicit move-ctor is added in g++ 4.6

    void swap(Buffer &rhs) {
        buffer_.swap(rhs.buffer_);
        std::swap(readerIndex_, rhs.readerIndex_);
        std::swap(writerIndex_, rhs.writerIndex_);
    }

    /**
     * 返回还有多少可读取的数据在缓冲区中
     * @return
     */
    size_t readableBytes() const {
        return writerIndex_ - readerIndex_;
    }

    /**
     * 返回缓冲区还有多少可写空间
     * @return
     */
    size_t writableBytes() const { return buffer_.size() - writerIndex_; }

    /**
     * 缓冲区前面剩余的空间
     * @return
     */
    size_t prependableBytes() const { return readerIndex_; }

    /**
     * 查看数组当前起始位置的元素
     * @return
     */
    const char *peek() const { return begin() + readerIndex_; }

    /**
     * 寻找回车换行符
     * @return
     */
    const char *findCRLF() const {
        // FIXME: replace with memmem()?
        const char *crlf = std::search(peek(), beginWrite(), kCRLF, kCRLF + 2);
        return crlf == beginWrite() ? NULL : crlf;
    }

    const char * findAnd() const{
        const char * and_ = std::find(peek(), beginWrite(), kAnd);
        return and_ == beginWrite() ? nullptr : and_;
    }

    /**
     * 从指定的起始位置开始搜索回车换行符
     * @param start
     * @return
     */
    const char *findCRLF(const char *start) const {
        assert(peek() <= start);
        assert(start <= beginWrite());
        // FIXME: replace with memmem()?
        const char *crlf = std::search(start, beginWrite(), kCRLF, kCRLF + 2);
        return crlf == beginWrite() ? NULL : crlf;
    }

    /**
     * 寻找换行符
     * @return
     */
    const char *findEOL() const {
        const void *eol = memchr(peek(), '\n', readableBytes());
        return static_cast<const char *>(eol);
    }

    const char *findEOL(const char *start) const {
        assert(peek() <= start);
        assert(start <= beginWrite());
        const void *eol = memchr(start, '\n', beginWrite() - start);
        return static_cast<const char *>(eol);
    }

    // retrieve returns void, to prevent
    // string str(retrieve(readableBytes()), readableBytes());
    // the evaluation of two functions are unspecified
    /**
     * 移动读取指针表示往后读取, 会检查len是否合法, len为要读取的数据的长度
     * @param len
     */
    void retrieve(size_t len) {
        assert(len <= readableBytes());
        if (len < readableBytes()) {
            readerIndex_ += len;
        } else {
            retrieveAll();
        }
    }

    /**
     * 检查结束坐标是否合法, 并移动读取指针
     * @param end
     */
    void retrieveUntil(const char *end) {
        assert(peek() <= end);
        assert(end <= beginWrite());
        retrieve(end - peek());
        // 检查可读位置和这个结束位置会不会超
    }

    void retrieveInt64() {
        retrieve(sizeof(int64_t));
    }

    void retrieveInt32() {
        retrieve(sizeof(int32_t));
    }

    void retrieveInt16() {
        retrieve(sizeof(int16_t));
    }

    void retrieveInt8() {
        retrieve(sizeof(int8_t));
    }

    /**
     * 此时缓冲区被读取完毕, 没有任何可写和可读内容
     */
    void retrieveAll() {
        readerIndex_ = kCheapPrepend;
        writerIndex_ = kCheapPrepend;
    }

    string retrieveAllAsString() {
        return retrieveAsString(readableBytes());
    }

    /**
     * 获取指定长度的字符串从缓冲区中, 内容是复制出来的, 影响缓冲区的输入输出指针
     * @param len
     * @return
     */
    string retrieveAsString(size_t len) {
        assert(len <= readableBytes());
        string result(peek(), len);
        retrieve(len);
        return result;
    }

    /*   StringPiece toStringPiece() const {
           return StringPiece(peek(), static_cast<int>(readableBytes()));
       }

       void append(const StringPiece &str) {
           append(str.data(), str.size());
       }
   */
    /**
     * 复制data内的数据到Buffer中
     * @param data 要拷贝的数据
     * @param len 要拷贝的数据的长度
     */
    void append(const char * /*restrict*/ data, size_t len) {
        ensureWritableBytes(len);
        std::copy(data, data + len, beginWrite());
        hasWritten(len);
    }

    void append(const void * /*restrict*/ data, size_t len) {
        append(static_cast<const char *>(data), len);
    }

    void append(const StringArg &str) {
        append(str.data(), str.size());
    }

    /**
     * 查看是否有足够的空间写入缓冲区
     * @param len 要检查的长度
     */
    void ensureWritableBytes(size_t len) {
        if (writableBytes() < len) {
            makeSpace(len);
        }
        assert(writableBytes() >= len);
    }

    /**
     * 返回可写缓冲区的起始坐标
     */
    char *beginWrite() { return begin() + writerIndex_; }

    const char *beginWrite() const { return begin() + writerIndex_; }

    /**
     * 写入后写入位置往后挪移
     * @param len
     */
    void hasWritten(size_t len) {
        assert(len <= writableBytes());
        writerIndex_ += len;
    }

    /**
     * 反向挪移写入位置
     * @param len
     */
    void unwrite(size_t len) {
        assert(len <= readableBytes());
        writerIndex_ -= len;
    }

    ///
    /// Append int64_t using network endian
    ///
/*
    void appendInt64(int64_t x) {
        int64_t be64 = sockets::hostToNetwork64(x);
        append(&be64, sizeof be64);
    }
*/

    ///
    /// Append int32_t using network endian
    ///
/*    void appendInt32(int32_t x) {
        int32_t be32 = sockets::hostToNetwork32(x);
        append(&be32, sizeof be32);
    }

    void appendInt16(int16_t x) {
        int16_t be16 = sockets::hostToNetwork16(x);
        append(&be16, sizeof be16);
    }*/

    void appendInt8(int8_t x) {
        append(&x, sizeof x);
    }

    ///
    /// Read int64_t from network endian
    ///
    /// Require: buf->readableBytes() >= sizeof(int32_t)
/*    int64_t readInt64() {
        int64_t result = peekInt64();
        retrieveInt64();
        return result;
    }*/

    ///
    /// Read int32_t from network endian
    ///
    /// Require: buf->readableBytes() >= sizeof(int32_t)
/*    int32_t readInt32() {
        int32_t result = peekInt32();
        retrieveInt32();
        return result;
    }

    int16_t readInt16() {
        int16_t result = peekInt16();
        retrieveInt16();
        return result;
    }

    int8_t readInt8() {
        int8_t result = peekInt8();
        retrieveInt8();
        return result;
    }*/

    ///
    /// Peek int64_t from network endian
    ///
    /// Require: buf->readableBytes() >= sizeof(int64_t)
    /* int64_t peekInt64() const {
         assert(readableBytes() >= sizeof(int64_t));
         int64_t be64 = 0;
         ::memcpy(&be64, peek(), sizeof be64);
         return sockets::networkToHost64(be64);
     }*/

    ///
    /// Peek int32_t from network endian
    ///
    /// Require: buf->readableBytes() >= sizeof(int32_t)
/*    int32_t peekInt32() const {
        assert(readableBytes() >= sizeof(int32_t));
        int32_t be32 = 0;
        ::memcpy(&be32, peek(), sizeof be32);
        return sockets::networkToHost32(be32);
    }*/

    /*   int16_t peekInt16() const {
           assert(readableBytes() >= sizeof(int16_t));
           int16_t be16 = 0;
           ::memcpy(&be16, peek(), sizeof be16);
           return sockets::networkToHost16(be16);
       }

       int8_t peekInt8() const {
           assert(readableBytes() >= sizeof(int8_t));
           int8_t x = *peek();
           return x;
       }
   */
    ///
    /// Prepend int64_t using network endian
    ///
    /*   void prependInt64(int64_t x) {
           int64_t be64 = sockets::hostToNetwork64(x);
           prepend(&be64, sizeof be64);
       }*/

    ///
    /// Prepend int32_t using network endian
    ///
    /* void prependInt32(int32_t x) {
         int32_t be32 = sockets::hostToNetwork32(x);
         prepend(&be32, sizeof be32);
     }*/

    /*   void prependInt16(int16_t x) {
           int16_t be16 = sockets::hostToNetwork16(x);
           prepend(&be16, sizeof be16);
       }

       void prependInt8(int8_t x) {
           prepend(&x, sizeof x);
       }
   */

    /**
     * 拷贝data中的数据到缓冲区中
     * @param data
     * @param len
     */
    void prepend(const void * /*restrict*/ data, size_t len) {
        assert(len <= prependableBytes());
        readerIndex_ -= len;
        const char *d = static_cast<const char *>(data);
        std::copy(d, d + len, begin() + readerIndex_);
    }

    /*   void shrink(size_t reserve) {
           // FIXME: use vector::shrink_to_fit() in C++ 11 if possible.
           Buffer other;
           other.ensureWritableBytes(readableBytes() + reserve);
           other.append(toStringPiece());
           swap(other);
       }*/

    size_t internalCapacity() const { return buffer_.capacity(); }

    /**
     * 调用 read 读取 fd 中到缓冲区中
     * @param fd 要读取的文件描述符
     * @param savedErrno 中间发生的错误
     */
    ssize_t readFd(int fd, int *savedErrno);

private:

    char *begin() { return &*buffer_.begin(); }

    const char *begin() const { return &*buffer_.begin(); }

    /**
     * 扩展空间, 保证长度为len的元素可以加入缓冲区
     * @param len
     */
    void makeSpace(size_t len) {
        if (writableBytes() + prependableBytes() < len + kCheapPrepend) {
            // 如果可写空间 + 前面因读取空出来的空间 < 长度 + 预留长度, 就直接扩容
            // FIXME: move readable data
            buffer_.resize(writerIndex_ + len);
        } else {
            // move readable data to the front, make space inside buffer
            assert(kCheapPrepend < readerIndex_);
            // 将元素往前移动保证元素
            size_t readable = readableBytes();
            std::copy(begin() + readerIndex_,
                      begin() + writerIndex_,
                      begin() + kCheapPrepend);
            readerIndex_ = kCheapPrepend;
            writerIndex_ = readerIndex_ + readable;
            assert(readable == readableBytes());
        }
    }

private:
    std::vector<char> buffer_;
    // 维护的缓冲区
    size_t readerIndex_;
    size_t writerIndex_;
    // 写入和写出位置下标
    static const char kCRLF[];
    // 回车换行符 CR = '\r', LF = '\n'
    static const char kAnd;
};

#endif //TCP_BUFFER_H
